#!/usr/bin/env python3
# -*- coding: utf-8 -*-

'''text to image'''

import os
import sys
import argparse
import re

import ctools.common.mycmd as mycmd
from ctools.fileworker import (get_fileworker_classes,)

from ctools.ly_parser.gvc_parser import GVCParser

from ctools.subcmd.ctbase import CToolsCmd

import logging
from ctools.logext import *

_rlog, _plog, _mlog, _tlog = get_loggers(__file__, __name__, propagate=False)
_prompt = 'CView=> '

_fpath = os.path.dirname(os.path.realpath(__file__))
_rcs_path = os.path.join(_fpath, '../rcs')

class CviewCli(mycmd.MyCmd):
    def __init__(self, prompt=_prompt,
                tryshell=False, autorep=False,
                comment_prefix='#') -> None:
        super().__init__(prompt=prompt,
                        tryshell=tryshell, autorep=autorep,
                        comment_prefix=comment_prefix)

        self.file_works = {}

    def __del__(self):
        while self.file_works:
            key, value = self.file_works.popitem()
            del value

        super().__del__()

    def create_file_processor(self, filename):
        match_cnt = 0
        for worker in get_fileworker_classes():
            if worker.is_match(filename):
                _plog.info(f"match {filename}, woker: {worker.__name__}")
                try:
                    fh = worker(filename)
                    fh.start_work()
                    if filename in self.file_works:
                        self.file_works[filename].append(fh)
                    else:
                        self.file_works[filename] = [fh]
                    match_cnt += 1
                except Exception as e:
                    _plog.exception(e)
        if match_cnt == 0:
            _plog.warning(f'no handler for {filename}')
        return match_cnt

    def _line2files(self, line):
        line = re.sub(r'\s+', ' ', line)
        if not line:
            return []

        if ' ' in line:
            return [os.path.realpath(x) for x in line.split(' ')]
        return [os.path.realpath(line)]

    def _do_handle(self, filename, func=None):
        if func:
            for worker in get_fileworker_classes():
                if not worker.is_match(filename):
                    continue
                func(worker, filename)

    def do_showfiles(self, line):
        for idx, filename in enumerate(self.file_works, start=1):
            print(f'{idx:>2d}: {filename}')

    def do_newfile(self, line):
        files = self._line2files(line)

        for f in files:
            if os.path.exists(f):
                _plog.warning(f'file {f} already exists')
            if f in self.file_works:
                _plog.warning(f'work {f} already exists')
                continue

            self._do_handle(f, func=lambda wk, fname: wk.handle_new(fname))

            ret = self.create_file_processor(f)
            if not ret:
                _plog.warning(f'failed to create work for {f}')

    def do_addfile(self, line):
        files = self._line2files(line)

        for f in files:
            if not os.path.exists(f):
                _plog.warning(f'{f} not exists')
                continue

            if f not in self.file_works:
                if not self.create_file_processor(f):
                    _plog.warning(f'failed to create work for {f}')
                    continue
                for wk in self.file_works[f]:
                    wk.do_modified()

    def _killfile(self, filename):
        while self.file_works[filename]:
            wk = self.file_works[filename].pop()
            del wk

    def do_killfile(self, line):
        files = self._line2files(line)

        for f in files:
            if f not in self.file_works:
                _plog.warning(f'{f} not exists')
                continue
            self._killfile(f)

    def do_rmfile(self, line):
        files = self._line2files(line)

        for f in files:
            _plog.info(f'remove file: {f}')
            if f in self.file_works:
                _plog.debug(f'delete file work: {f}')
                self._killfile(f)

            self._do_handle(f, func=lambda wk, fname: wk.handle_remove(fname))

    def do_dumpfile(self, line):
        files = self._line2files(line)

        for filename in files:
            if not os.path.exists(filename):
                _plog.warning(f'{filename} not exists')
                continue
            self._do_handle(filename, func=lambda wk, fname: wk.handle_dump(fname))

class CViewCmd(CToolsCmd):
    @classmethod
    def name(cls):
        return "cview"

    @classmethod
    def description(cls):
        return "cview cli tool"

    def do_command(self, argv:list[str]):
        argp = argparse.ArgumentParser(description='cview cli tool')
        argp.add_argument('-n', '--no-interactive', dest='no_interactive', action='store_true',
                            help="need files")
        argp.add_argument('files', nargs='*')

        args = argp.parse_args(argv)

        cli = CviewCli(prompt=_prompt)

        if args.no_interactive:
            if not args.files:
                argp.print_help()
                return

        if args.files:
            cli.do_addfile(' '.join(args.files))
            if args.no_interactive:
                return

        return cli.cmdloop()
